package org.windowkit.toolkit.widget.combo;

import java.util.Comparator;

import javax.swing.AbstractListModel;

import org.ddevil.data.Data;
import org.ddevil.data.LightweightAttributes;
import org.ddevil.data.SortOrder;
import org.ddevil.data.StringNaturalOrderComparator;
import org.ddevil.data.set.BasicDataSet;
import org.ddevil.data.set.BasicSortableDataSet;
import org.ddevil.data.set.DataSet;


/**
 * A combo box model that wraps a DataSet.
 *
 * @see {@link org.ddevil.data.set.DataSet}
 *
 * @author Rich
 *
 */
public class DataSetComboModel extends AbstractListModel implements AdvancedComboModel {


    /**
	 *
	 */
	private static final long serialVersionUID = 1769372130407072369L;
	Object selectedObject;
    BasicSortableDataSet model;
    String displayID;
    Comparator labelComparator;

    private static final BasicSortableDataSet EMPTY_DATA_SET = new BasicSortableDataSet(new BasicDataSet(new String[0]));

    public DataSetComboModel(){
        this(EMPTY_DATA_SET, "");
    }

    public DataSetComboModel(final String displayID){
        this(EMPTY_DATA_SET, displayID);
    }

    /**
     * Constructs an empty DefaultComboBoxModel object.
     */
    public DataSetComboModel(final DataSet model, final String displayID) {
        this.labelComparator = StringNaturalOrderComparator.instance();
        this.displayID = displayID;
        this.model = wrapModel(model);
    }

    public void setDataSet(final DataSet model, final String id){
        this.model  = wrapModel(model);
        this.displayID = id;
        fireContentsChanged(this, 0, this.model.size());
    }

    public void setDataSet(final DataSet model){
        this.model  = wrapModel(model);
        fireContentsChanged(this, 0, this.model.size());
    }

    private BasicSortableDataSet wrapModel(final DataSet in){
        BasicSortableDataSet sds = new BasicSortableDataSet(in);
        sds.setComparator(displayID, labelComparator);
        sds.setSort(displayID, SortOrder.ASCENDING);
        return sds;
    }

    public void dataChanged(){
        model.resetSort();
        fireContentsChanged(this, 0, this.model.size());
    }

    public void setDisplayID(final String id){
        this.displayID = id;
        fireContentsChanged(this, 0, this.model.size());
    }


    // implements javax.swing.ComboBoxModel
    /**
     * Set the value of the selected item. The selected item may be null.
     * <p>
     * @param anObject The combo box value or null for no selection.
     */
    public void setSelectedItem(final Object anObject) {
        if ((selectedObject != null && !selectedObject.equals(anObject)) ||
                selectedObject == null && anObject != null) {
            selectedObject = anObject;
            fireContentsChanged(this, -1, -1);
        }
    }

    // implements javax.swing.ComboBoxModel
    public Object getSelectedItem() {
        return selectedObject;
    }

    // implements javax.swing.ListModel
    public int getSize() {
        return model != null ? model.size() : 0;
    }

    // implements javax.swing.ListModel
    public Object getElementAt(final int index) {
        if ( index >= 0 && index < getSize() ) {
			return model.getDataAt(index, displayID);
		}
		else {
			return null;
		}
    }

    /**
     * Returns the index-position of the specified object in the list.
     *
     * @param anObject
     * @return an int representing the index position, where 0 is
     *         the first position
     */
    public int getIndexOf(final Object anObject) {
        int index = -1;
        for(int i = 0; i < getSize(); i++){
            if(model.getDataAt(i, displayID).equals(anObject)){
                index = i;
                break;
            }
        }
        return index;
    }

    // implements javax.swing.MutableComboBoxModel
    public void addElement(final Object anObject) {
        if(anObject != null){
            //add a new record whose dispaly attribute will be the new object
            //all other attributes will be null
            model.importRecord(new LightweightAttributes(displayID, anObject));

            //we cant gaurantee where the item will be added
            fireIntervalAdded(this, 0, model.size()-1);
            if ( model.size() == 1 && selectedObject == null && anObject != null ) {
                setSelectedItem(anObject);
            }
        }
    }

    // implements javax.swing.MutableComboBoxModel
    public void insertElementAt(final Object anObject, final int index) {
        //XXX not supporting this for now
        addElement(anObject);
    }

    // implements javax.swing.MutableComboBoxModel
    public void removeElementAt(final int index) {
        if(getElementAt(index) == selectedObject) {
            if (index == 0) {
                setSelectedItem(getSize() == 1 ? null : getElementAt(index + 1));
            }
            else {
                setSelectedItem(getElementAt(index - 1));
            }
        }

        model.removeRecord(index);

        fireIntervalRemoved(this, index, index);
    }

    // implements javax.swing.MutableComboBoxModel
    public void removeElement(final Object anObject) {
        int index = getIndexOf(anObject);
        if(index != -1){
            removeElementAt(index);
        }
    }

    /**
     * Empties the list.
     */
    public void removeAllElements() {
        selectedObject = null;
        if ( model.size() > 0 ) {
            int firstIndex = 0;
            int lastIndex = model.size() - 1;
            this.model = EMPTY_DATA_SET;
            fireIntervalRemoved(this, firstIndex, lastIndex);
        }
    }

    /*
     * DataSet specific stuff
     *
     */

    public Data getSelectedData(){
        Data ret = null;
        if(selectedObject != null){
            int index = getIndexOf(selectedObject);
            if(index > -1){
                ret = model.exportRecord(index);
            }
        }
        return ret;
    }

    public void addData(final Data in){
        if(in != null){
            model.importRecord(in);
            //we cant gaurantee where the item will be added
            fireIntervalAdded(this, 0, model.size()-1);
        }
    }

    /**
     * Set the comparator to use when sorting the labels.
     * Default is {@link StringNaturalOrderComparator}.
     *
     * @param labelComparator
     */
    public void setLabelComparator(final Comparator labelComparator) {
        this.labelComparator = labelComparator;
        this.model.setComparator(displayID, this.labelComparator);
        this.model.resetSort();
        fireContentsChanged(this, 0, this.model.size());
    }


}
